"""
xapper assembly and Silverlight source finder.
"""

import os

from django.core.exceptions import ImproperlyConfigured
from xapper.conf import settings


def _get_finders(finder_list):
  """Get the finder objects specified by a list of finder object paths.

  Args:
    finder_list - (unicode) The name of the setting attribute containing the
                  list of finder object paths (e.g. 'package.module.object')
                  to be used to find files.

  Yields:
    (series of object) The finder objects.
  """
  finder_paths = getattr(settings, finder_list)
  for finder_path in finder_paths:
    module, attr = finder_path.rsplit(".", 1)
    try:
      mod = __import__(module, globals(), locals(), [attr])
    except ImportError as e:
      raise ImproperlyConfigured("Error importing finder object {0}: '{1}'" .format(module, e))
    try:
      finder = getattr(mod, attr)
    except AttributeError as e:
      raise ImproperlyConfigured("Module '{0}' does not define a '{1}' finder object."
                                 .format(module, attr))
    try:
      func = getattr(finder, "find")
    except AttributeError as e:
      raise ImproperlyConfigured("Finder object '{0}' does not define a 'find' method."
                                 .format(finder_path))
    yield finder


def finder_func(finder_list, filter_func=None):
  """Create a finder function that uses a given list of finder objects.

  Args:
    finder_list - (unicode) The name of the setting attribute containing
                  the list of finder object paths (e.g.
                  'package.module.object') to be used to find files.
    filter_func - (callable) A function to filer the results.

  Returns:
    A finder function that takes a single relative path argument and
    returns an iterable of the absolute paths all the finder objects find.
  """
  finders = list(_get_finders(finder_list))
  def find(path):
    """Search the finder objects specified in the {0}
    setting for a relative path.

    Args:
      path - (unicode) The relative path to search for.

    Yields:
      A series of strings containing the absolute paths of all filesystem
      entities (files, dirs, etc.) that were found by the finder objects for
      which the filter function `{1}` returns True.
    """
    for finder in finders:
      for found in finder.find(path):
        if filter_func is None or filter_func(found):
          yield found
  find.__doc__ = find.__doc__.format(finder_list, filter_func.__name__)
  return find


find_assembly = finder_func("XAPPER_ASSEMBLY_FINDERS", os.path.isfile)
find_app_dir = finder_func("XAPPER_SILVERLIGHT_APP_FINDERS", os.path.isdir)
